package com.wxmit.framework.excel.lintener;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.holder.ReadSheetHolder;
import com.wxmit.base.exception.excel.ExcelReadValidException;
import com.wxmit.base.validate.AddGroup;
import org.hibernate.validator.HibernateValidator;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import java.util.List;
import java.util.Objects;
import java.util.Set;


/**
 * ExcelReadValidListener
 *  导入excel的 校验监听类。
 * @author huangxiong
 * @version V1.0
 * @date 2023/1/6 15:42
 */
public class ExcelReadValidListener<T> implements ReadListener<T> {

    private static volatile Validator validator;
    ExcelReadValidException readValidException;

    /**
     *  快速失败
     *  true 则 异常情况下 马上会返回。
     *  false 则 校验完所有数据再返回（遇到数据转换异常，会影响对象属性校验）。
     */
    private Boolean fastFail = false;

    private List<T> list ;

    public ExcelReadValidListener(){}
    public ExcelReadValidListener(Boolean fastFail,List<T> list){
        this.fastFail = fastFail;
        this.list = list;
    }


    @Override
    public void invoke(T t, AnalysisContext context) {
        Set<ConstraintViolation<T>> validate = getValidator().validate(t, AddGroup.class, Default.class);
        if(CollUtil.isNotEmpty(validate)){
            ReadSheetHolder readSheetHolder = context.readSheetHolder();
            Integer rowIndex = readSheetHolder.getRowIndex();
            validate.forEach(it->{
                putException(rowIndex+1,null,it.getMessage());
            });
            isThrowException();
        }else{
            putList(t);
        }
    }



    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // 是否数据转换的错误
        if(exception instanceof ExcelDataConvertException){
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            Throwable msgThrowable = getLastCause(excelDataConvertException);
            Integer rowNum = excelDataConvertException.getRowIndex() ;
            Integer cellNum = excelDataConvertException.getColumnIndex() ;
            putException(rowNum,cellNum,msgThrowable.getMessage());
        }else{
            readValidException.setMessage(exception.getMessage());
        }
        isThrowException();

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if(Objects.nonNull(readValidException))
            throw readValidException;
    }

    public void putList(T t){
        if(Objects.nonNull(list))
            list.add(t);
    }
    private void putException(Integer rowNum, Integer cellNum, String msg){
        if(Objects.isNull(readValidException)){
            readValidException = new ExcelReadValidException();
        }
        ExcelReadValidException.ValidError ve =new ExcelReadValidException.ValidError(rowNum,cellNum,msg);
        readValidException.putValidError(ve);
    }
    /**
     * 获取最底层包裹吨异常对象
     * @param e 异常对象
     * @return 最底层的异常对象
     */
    private Throwable getLastCause(Throwable e){
        if(Objects.isNull(e.getCause())){
            return e;
        }
        return getLastCause(e.getCause());
    }

    public Validator getValidator(){
        if(Objects.isNull(validator)){
            ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory();
            if(validator==null)
                validator = validatorFactory.getValidator();
        }
        return validator;
    }
    private void isThrowException() {
        //抛出异常
        if(fastFail)
            throw readValidException;
    }
}
